package com.stars.easyms.mq.autoconfigure;

import com.stars.easyms.base.util.SpringBootUtil;
import com.stars.easyms.datasource.EasyMsMultiDataSource;
import com.stars.easyms.datasource.batch.BatchCommit;
import com.stars.easyms.datasource.interceptor.mybatis.EasyMsQueryInterceptor;
import com.stars.easyms.datasource.interceptor.mybatis.EasyMsUpdateInterceptor;
import com.stars.easyms.datasource.mybatis.EasyMsMybatisConfiguration;
import com.stars.easyms.datasource.mybatis.EasyMsSqlSessionFactoryBean;
import com.stars.easyms.datasource.util.XmlMapperParseUtil;
import com.stars.easyms.mq.core.EasyMsMQSender;
import com.stars.easyms.mq.initializer.MQRepositoryInitializer;
import com.stars.easyms.mq.initializer.MQManagerInitializer;
import com.stars.easyms.mq.pointcut.MQSenderPointcutAdvisor;
import com.stars.easyms.mq.properties.EasyMsMQProperties;
import com.stars.easyms.mq.service.MQMessageService;
import com.stars.easyms.mq.util.EasyMsMQPackageUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.util.Assert;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * MQ自动配置类
 *
 * @author guoguifang
 * @date 2018-04-23 13:54
 * @since 1.0.0
 */
@Slf4j
@Configuration
@EnableConfigurationProperties(EasyMsMQProperties.class)
public class EasyMsMQAutoConfiguration {

    private ConfigurableApplicationContext applicationContext;

    private String applicationId;

    private EasyMsMQProperties easyMsMQProperties;

    private EasyMsMultiDataSource dataSource;

    private String xmlMapperLocation;

    public EasyMsMQAutoConfiguration(ConfigurableApplicationContext applicationContext, EasyMsMQProperties easyMsMQProperties,
                                     ObjectProvider<EasyMsMultiDataSource> dataSourceProvider, ObjectProvider<BatchCommit> batchCommitDAOProvider) throws IOException {
        this.applicationContext = applicationContext;

        // 获取数据源以及批量提交DAO
        this.dataSource = dataSourceProvider.getIfAvailable();
        BatchCommit batchCommit = batchCommitDAOProvider.getObject();
        this.xmlMapperLocation = "classpath*:" + EasyMsMQPackageUtil.getRepositoryPackageName().replace(".", "/") + "/**/*Mapper.xml";

        // 获取MQ的属性配置并进行校验
        this.easyMsMQProperties = easyMsMQProperties;
        this.easyMsMQProperties.check();

        if (this.easyMsMQProperties.isEnabled()) {
            this.applicationId = this.easyMsMQProperties.getApplicationId();
            if (StringUtils.isBlank(this.applicationId)) {
                this.applicationId = SpringBootUtil.getApplicationName();
                Assert.isTrue(StringUtils.isNotBlank(this.applicationId), "Please configure the application id with [" + EasyMsMQProperties.PREFIX + ".application-id]!");
            }
            this.applicationId = this.applicationId.toLowerCase();

            // 将MQ的相关SQL添加到可批量提交列表
            XmlMapperParseUtil.parse(batchCommit.getSqlSessionFactory(), this.xmlMapperLocation);
        } else {
            log.warn("MQ module is disabled, if need to enable, please config [{}.enabled=true] in the properties or yaml file!", EasyMsMQProperties.PREFIX);
        }
    }

    @Bean(value = "easyMsMQManagerInitializer", initMethod = "init")
    @DependsOn("easyMsMQRepositoryInitializer")
    public MQManagerInitializer mqManagerInitializer(MQMessageService mqMessageService) {
        return new MQManagerInitializer(applicationContext, applicationId, easyMsMQProperties, mqMessageService);
    }

    @Bean(value = "easyMsMQRepositoryInitializer", initMethod = "init")
    @DependsOn("easyMsMQSqlSessionFactory")
    public MQRepositoryInitializer mqRepositoryInitializer() {
        return new MQRepositoryInitializer(applicationContext);
    }

    @Bean("easyMsMQSqlSessionFactory")
    public SqlSessionFactory easyMsMQSqlSessionFactory(ObjectProvider<EasyMsUpdateInterceptor> easyMsUpdateInterceptorObjectProvider,
                                                       ObjectProvider<EasyMsQueryInterceptor> easyMsQueryInterceptorObjectProvider) throws Exception {
        EasyMsSqlSessionFactoryBean sqlSessionFactoryBean = new EasyMsSqlSessionFactoryBean(new EasyMsMybatisConfiguration());
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setTypeAliasesPackage(EasyMsMQPackageUtil.getRepositoryPackageName());
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(xmlMapperLocation));
        List<Interceptor> interceptors = new ArrayList<>();
        EasyMsUpdateInterceptor easyMsUpdateInterceptor = easyMsUpdateInterceptorObjectProvider.getIfAvailable();
        if (easyMsUpdateInterceptor != null) {
            interceptors.add(easyMsUpdateInterceptor);
        }
        EasyMsQueryInterceptor easyMsQueryInterceptor = easyMsQueryInterceptorObjectProvider.getIfAvailable();
        if (easyMsQueryInterceptor != null) {
            interceptors.add(easyMsQueryInterceptor);
        }
        if (!interceptors.isEmpty()) {
            sqlSessionFactoryBean.setPlugins(interceptors.toArray(new Interceptor[0]));
        }
        return sqlSessionFactoryBean.getObject();
    }

    @Bean
    public MQMessageService mqMessageService() {
        return new MQMessageService(easyMsMQProperties);
    }

    @Bean
    public EasyMsMQSender easyMsMQSender(MQMessageService mqMessageService) {
        return new EasyMsMQSender(mqMessageService, applicationId, easyMsMQProperties);
    }

    @Bean
    public MQSenderPointcutAdvisor mqSenderPointcutAdvisor(EasyMsMQSender easyMsMQSender) {
        return new MQSenderPointcutAdvisor(easyMsMQSender, easyMsMQProperties);
    }

}
